#include "sock.h"

void bk_writetoserver (int filedes, char * message, unsigned int length)
{
  int nbytes;
     
  nbytes = write (filedes, message, length);
  if (nbytes < 0)
    {
      printf ("error write\n");
      return ;
    }
}

sockparams * bk_clientinit(unsigned int m_port, char * m_serverhost)
{
  sockparams * s = malloc(sizeof(sockparams));
  s->port = m_port;

  /* Create the socket. */
  s->sock = socket (AF_INET, SOCK_STREAM, 0);
  if (  s->sock < 0)
    {
      printf ("error socket (client)\n");
      return -1;
    }
  fcntl(s->sock, F_SETFL, O_NONBLOCK);
  //  s->wait.tv_sec = m_sec;
  //  s->wait.tv_usec = m_usec;
     
  /* Connect to the server. */
  bk_initsockaddr (&(s->servername), m_serverhost, m_port);
  return s;
}

void bk_clientfinalize(sockparams * m_params)
{
  close (m_params->sock);
  free(m_params);
}
     
int bk_clientconnect(sockparams * m_params)
{
  if (0 > connect (m_params->sock, (struct sockaddr *) &(m_params->servername), sizeof (m_params->servername)))
    {
      printf ("error connect (client)\n");
      return -2;
    }
     
  return 0;
}
     
struct sockbuf * bk_readfromclient (int filedes, int sz)
{
  struct sockbuf * sbuf = malloc(sizeof(sockbuf));
  sbuf->buf = malloc(sz);
  //  memset(sbuf->buf, 0, sz);
  sbuf->size = read (filedes, sbuf->buf, sz);

  if (sbuf->size < 0)
    {
      /* Read error. */
      free(sbuf->buf);
      free(sbuf);
      printf ("error read\n");
      return NULL;
    }
  else if (sbuf->size == 0)
    {
      /* End-of-file. */
      //      printf ("EOF\n");
      free(sbuf->buf);
      free(sbuf);
      return NULL;
    }
  return sbuf;
}

sockparams * bk_serverinit(unsigned int m_port, int m_sec, int m_usec)
{
  sockparams * s = malloc(sizeof(sockparams));
  /* Create the socket and set it up to accept connections. */
  s->sock = bk_makesocket (m_port);
  fcntl(s->sock, F_SETFL, O_NONBLOCK);
  s->wait.tv_sec = m_sec;
  s->wait.tv_usec = m_usec;
  if (listen (s->sock, 1) < 0)
    {
      printf ("error listen\n");
      return NULL;
    }

  /* Initialize the set of active sockets. */
  FD_ZERO (&(s->active_fd_set));
  FD_SET (s->sock, &(s->active_fd_set));
     
  return s;
}

void bk_serverfinalize(struct sockparams * m_serv)
{
  close(m_serv->sock);
  free(m_serv);
}
     
int bk_serverstep (struct sockparams * m_serv, int m_stepsz)
{
  /* Block until input arrives on one or more active sockets. */
  m_serv->read_fd_set = m_serv->active_fd_set;
  if (select (FD_SETSIZE, &(m_serv->read_fd_set), NULL, NULL, &m_serv->wait) < 0)
    {
      printf ("error select\n");
      return -1;
    }

  int i=0;
  size_t size;
  struct sockbuf * sbuf = NULL;
  struct sockaddr_in clientname;
  /* Service all the sockets with input pending. */
  for (i = 0; i < FD_SETSIZE; ++i)

    if (FD_ISSET (i, &(m_serv->read_fd_set)))
      {
	if (i == m_serv->sock)
	  {
	    printf ("fd = %i\n",i);
	    printf ("accepting\n");
	    /* Connection request on original socket. */
	    int new;
	    size = sizeof (clientname);
	    m_serv->sockin = accept (m_serv->sock,(struct sockaddr *) &clientname, &size);
	    printf ("accepted\n");
	    if (m_serv->sockin  < 0)
	      {
		printf ("error accept\n");
		return 0;
	      }
	    printf ("setting fd\n");
	    //	    printf ("Server: connect from host %s, port %i.\n", inet_ntoa (clientname.sin_addr), ntohs (clientname.sin_port));
	    FD_SET (m_serv->sockin , &(m_serv->active_fd_set));
	  }
	else
	  {
	    /* Data arriving on an already-connected socket. */
	    sbuf = bk_readfromclient (i,m_stepsz);
	    if (sbuf == NULL)
	      {
		printf ("closing socket\n");
		close(i);
		FD_CLR (i, &(m_serv->active_fd_set));
	      }
	    else
	      {
		//		printf ("read ! \n");
		sbuf->apppar=m_serv->apppar;
		m_serv->callback(sbuf);
		free(sbuf->buf);
		free(sbuf);
	      }
	  }
      }
}

int bk_makesocket (uint16_t port)
{
  int sock;
  struct sockaddr_in name;
     
  /* Create the socket. */
  sock = socket (AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
    {
      printf ("error socket");
      return -1;
    }
     
  /* Give the socket a name. */
  name.sin_family = AF_INET;
  name.sin_port = htons (port);
  name.sin_addr.s_addr = htonl (INADDR_ANY);
  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
    {
      printf ("error bind");
      return -2;
    }
     
  return sock;
}

void bk_initsockaddr (struct sockaddr_in *name, const char *hostname, uint16_t port)
{
  struct hostent *hostinfo;
     
  name->sin_family = AF_INET;
  name->sin_port = htons (port);
  hostinfo = gethostbyname (hostname);
  if (hostinfo == NULL)
    {
      fprintf (stderr, "Unknown host %s.\n", hostname);
      return;
    }
  name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
}
